package com.luych.toolbox.common.utils;

import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;

public class IpUtils {

    /**
     * PING一个IP地址
     *
     * @param ip IP地址
     * @return true通false不通
     */
    public static boolean ping(String ip) {
        try {
            return InetAddress.getByName(ip).isReachable(1000);
        } catch (IOException e) {
            return false;
        }
    }

    public static IpUtilIntf of(IpVersion version) {
        switch (version) {
            case IPV4: return new IpUtilIpv4();
            case IPV6: return new IpUtilIpv6();
            default: throw new UnsupportedOperationException();
        }
    }

    public interface IpUtilIntf {
        /**
         * 将xxx.xxx.xxx.xxx的IP转换为数字
         *
         * @param ip xxx.xxx.xxx.xxx
         * @return 数字
         */
        BigInteger convertIp(String ip);

        /**
         * 将数字转换为xxx.xxx.xxx.xxx的IP
         *
         * @param ip 数字
         * @return xxx.xxx.xxx.xxx的IP
         */
        String convertIp(BigInteger ip);

        /**
         * 将xxx.xxx.xxx.xxx的掩码转为数字，例如：255.255.255.0转成24
         *
         * @param mask xxx.xxx.xxx.xxx的掩码
         * @return 数字
         */
        int convertMask(String mask);

        /**
         * 将数字转为xxx.xxx.xxx.xxx的掩码，例如：24转成255.255.255.0
         *
         * @param mask 数字
         * @return xxx.xxx.xxx.xxx的掩码
         */
        String convertMask(long mask);

        /**
         * 将CIDR转为起始IP和终止IP
         *
         * @param cidr CIDR
         * @return 第一个元素为起始IP，第二个元素为终止IP
         */
        String[] convertCidr(String cidr);

        /**
         * 枚举起始IP到终止IP中包含的IP地址
         *
         * @param startIp 起始IP
         * @param endIp   终止IP
         * @return 返回包含的IP地址
         */
        List<String> listIps(String startIp, String endIp);

        /**
         * 计算起始IP到终止IP中包含的IP地址数量
         *
         * @param startIp 起始IP
         * @param endIp   终止IP
         * @return 返回包含的IP地址数量
         */
        BigInteger countIps(String startIp, String endIp);

    }

    private static class IpUtilIpv4 implements IpUtilIntf {

        @Override
        public BigInteger convertIp(String ip) {
            String[] split = ip.split("\\.");
            String ip1 = split[0];
            String ip2 = split[1];
            String ip3 = split[2];
            String ip4 = split[3];
            long value = (Long.parseLong(ip1) << 24) +
                    (Long.parseLong(ip2) << 16) +
                    (Long.parseLong(ip3) << 8) +
                    Long.parseLong(ip4);
            return BigInteger.valueOf(value);
        }

        @Override
        public String convertIp(BigInteger ip) {
            String binaryString = Long.toBinaryString(ip.longValue());
            if (binaryString.length() < 32) {
                binaryString = "00000000000000000000000000000000" + binaryString;
                binaryString = binaryString.substring(binaryString.length() - 32);
            }
            String ip1 = binaryString.substring(0, 8);
            String ip2 = binaryString.substring(8, 16);
            String ip3 = binaryString.substring(16, 24);
            String ip4 = binaryString.substring(24, 32);
            return Integer.parseInt(ip1, 2) + "." +
                    Integer.parseInt(ip2, 2) + "." +
                    Integer.parseInt(ip3, 2) + "." +
                    Integer.parseInt(ip4, 2);
        }

        @Override
        public int convertMask(String mask) {
            long maskValue = convertIp(mask).longValue();
            String binaryString = Long.toBinaryString(maskValue);
            binaryString = binaryString.replace("0", "");
            return binaryString.length();
        }

        @Override
        public String convertMask(long mask) {
            mask = 32 - mask;
            mask = ((1L << 32) - 1) >> mask << mask;
            return convertIp(BigInteger.valueOf(mask));
        }

        @Override
        public String[] convertCidr(String cidr) {
            long gateway = convertIp(cidr.split("/")[0]).longValue();
            long mask = Long.parseLong(cidr.split("/")[1]);
            long mask10 = ((1L << mask) - 1) << (32 - mask);
            long mask01 = (1L << (32 - mask)) - 1;
            long minIp = gateway & mask10;
            long maxIp = gateway | mask01;
            String minIpStr = convertIp(BigInteger.valueOf(minIp));
            String maxIpStr = convertIp(BigInteger.valueOf(maxIp));
            return new String[]{minIpStr, maxIpStr};
        }

        @Override
        public List<String> listIps(String startIp, String endIp) {
            long startLong = convertIp(startIp).longValue();
            long endLong = convertIp(endIp).longValue();
            List<String> ips = new ArrayList<>();
            for (long i = startLong; i <= endLong; i++) {
                ips.add(convertIp(BigInteger.valueOf(i)));
            }
            return ips;
        }

        @Override
        public BigInteger countIps(String startIp, String endIp) {
            long startLong = convertIp(startIp).longValue();
            long endLong = convertIp(endIp).longValue();
            return BigInteger.valueOf(endLong - startLong);
        }
    }

    private static class IpUtilIpv6 implements IpUtilIntf {

        /**
         * 将简写的IPV6地址补全
         * 1. IPV6地址总长度为128比特，分为8个用冒号分隔的组，每组为4个十六进制数
         * 2. 每组中的前导0都可以省略
         * 3. 连续两个或多个均为0的组，可以用双冒号::来代替
         *
         * @param ip 简写IPV6地址
         * @return 补全IPV6地址
         */
        private String formatIp(String ip) {
            String[] split = ip.split(":");
            List<String> items = new ArrayList<>();
            for (String item : split) {
                item = "0000" + item;
                item = item.substring(item.length() - 4);
                items.add(item);
            }
            for (int i = items.size(); i < 8; i++) {
                items.add("0000");
            }
            return String.join(":", items);
        }

        @Override
        public BigInteger convertIp(String ip) {
            System.out.println("before: " + ip);
            ip = formatIp(ip);
            System.out.println("after: " + ip);
            return new BigInteger(ip.replace(":", ""), 16);
        }

        @Override
        public String convertIp(BigInteger ip) {
            String hex = ip.toString(16);
            hex = hex.substring(hex.length() - 32);
            return hex.substring(0, 4) + ":" +
                    hex.substring(4, 8) + ":" +
                    hex.substring(8, 12) + ":" +
                    hex.substring(12, 16) + ":" +
                    hex.substring(16, 20) + ":" +
                    hex.substring(20, 24) + ":" +
                    hex.substring(24, 28) + ":" +
                    hex.substring(28);
        }

        @Override
        public int convertMask(String mask) {

            return 0;
        }

        @Override
        public String convertMask(long mask) {
            mask = 32 - mask;
            mask = ((1L << 32) - 1) >> mask << mask;
            return convertIp(BigInteger.valueOf(mask));
        }

        @Override
        public String[] convertCidr(String cidr) {
            return new String[0];
        }

        @Override
        public List<String> listIps(String startIp, String endIp) {
            return null;
        }

        @Override
        public BigInteger countIps(String startIp, String endIp) {
            return null;
        }


    }

    private enum IpVersion {
        IPV4, IPV6
    }
}
